/*
 * Copyright (c) 2004-2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 *
 * Date:    April 2011
 */

#include "base/intmath.hh"
#include "base/bitfield.hh"
#include "cpu/edge/pred/twostage_framework.hh"
#include "cpu/edge/pred/global_type.hh"
#include "cpu/edge/pred/piecewise.hh"
#include "cpu/edge/pred/tour_framework_binary.hh"
#include "cpu/edge/pred/tage_binary.hh"
#include "cpu/base.hh"

template<class Impl>
TwostageFramework<Impl>::TwostageFramework(DerivEdgeCPUParams *params) :
    predictorMode(InvalidMode)
{
    initVars();

    if (params->btypeTwostagePredictionMode == "PiecewiseAndHyst") {

        DPRINTF(EdgePredUnit, "TSF: piecewise/hyst mode enabled.\n");

        predictorMode = Normal;

        stage1 = new PiecewiseBP<Impl>(params);

        // A table indexed only by address is a simple hyst-based
        // table.
        stage2 = new GlobalTypeP<Impl>(params, "Address");

    } else if (params->btypeTwostagePredictionMode == "TourAndHyst") {

        DPRINTF(EdgePredUnit, "TSF: tour/hyst mode enabled.\n");

        predictorMode = Normal;

        stage1 = new TourFrameworkBinary<Impl>(params);

        // A table indexed only by address is a simple hyst-based
        // table.
        stage2 = new GlobalTypeP<Impl>(params, "Address");

    } else if (params->btypeTwostagePredictionMode == "TAGEAndHyst") {

        DPRINTF(EdgePredUnit, "TSF: tage/hyst mode enabled.\n");

        predictorMode = Normal;

        stage1 = new TAGEBinary<Impl>(params);

        // A table indexed only by address is a simple hyst-based
        // table.
        stage2 = new GlobalTypeP<Impl>(params, "Address");

    } else if (params->btypeTwostagePredictionMode == "PerfectSeqAndHyst") {

        DPRINTF(EdgePredUnit, "TSF: perfect/hyst mode enabled.\n");

        predictorMode = PerfectSequential;

        stage1 = NULL;

        // A table indexed only by address is a simple hyst-based
        // table.
        stage2 = new GlobalTypeP<Impl>(params, "Address");

    } else {
        fatal("Invalid prediction mode selected for tournament type"
                " predictor\n");
    }
}

template<class Impl>
TwostageFramework<Impl>::~TwostageFramework()
{
    delete stage1;
    delete stage2;
}

template<class Impl>
void
TwostageFramework<Impl>::initVars()
{
    DPRINTF(EdgePredUnit, "TSF: Nothing to init right now.\n");
}

template<class Impl>
void
TwostageFramework<Impl>::reset()
{
    if (predictorMode == Normal) {
        stage1->reset();
    }
    stage2->reset();
}

template<class Impl>
void
TwostageFramework<Impl>::regStats()
{
    if (predictorMode == Normal) {
        stage1->regStats();
    }
    stage2->regStats();
}

template<class Impl>
TheISA::BTypeID
TwostageFramework<Impl>::lookup(BlockPtr & block, BlockID block_id,
        BlockID oldest_block_id, Addr block_pc, int addr_space_id,
        ThreadID tid)
{
    BTypeID prediction = TheISA::InvalidExitType;

    bool is_taken = false;

    if (predictorMode == PerfectSequential) {

        BTypeID real_type = block->getPredExitType();
        
        if (real_type != TheISA::seq) {
            is_taken = true;
        } else {
            is_taken = false;
        }

        DPRINTF(EdgePredUnit, "TSF: In perfect seqential mode."
                " Real type: %i, is_taken: %i\n", real_type, is_taken);

    } else {

        DPRINTF(EdgePredUnit, "TSF: In normal mode.\n");

        is_taken = stage1->lookup(block_id,
                                  oldest_block_id,
                                  block_pc,
                                  tid);
    }

    if (is_taken) {
        prediction =
            stage2->lookup(block_id,
                           oldest_block_id, 
                           block_pc,
                           addr_space_id,
                           tid);

        DPRINTF(EdgePredUnit, "Non-seqential target.\n");

    } else {
        prediction = TheISA::seq;

        DPRINTF(EdgePredUnit, "Sequential target.\n");
    }

    return prediction;
}

template<class Impl>
void
TwostageFramework<Impl>::update(BlockID block_id, Addr block_pc, 
        int addr_space_id, BTypeID actual_type_id, ThreadID tid, 
        bool seq_mispredicted)
{
    bool is_taken = (actual_type_id == TheISA::seq) ? false : true;

//    if (seq_mispredicted) {
//        DPRINTF(EdgePredUnit, "TSF: [tid:%i]: Sequential target is" 
//                " mispredicted, squashing the branch type predictor\n",
//                tid);

    // **ATTENTION** Since we don't check Exit IDs and/or Types on an update,
    // there is no need to squash the histories and/or paths.
//    stage1->squash(block_pc, 
//                   block_id,
//                   is_taken,
//                   tid);
//    }

    if (predictorMode == Normal) {
        stage1->update(block_id,
                       block_pc, 
                       tid,
                       is_taken,
                       seq_mispredicted);
    }

    if (is_taken) {
        stage2->update(block_id,
                       block_pc,
                       addr_space_id,
                       actual_type_id,
                       tid);
    }

}

template<class Impl>
void
TwostageFramework<Impl>::squash(BlockID block_id, Addr block_pc, 
        BTypeID type_id, int addr_space_id, ThreadID tid, bool seq_mispredicted)
{
    bool is_taken = (type_id == TheISA::seq) ? false : true;

//    if (seq_mispredicted) {
//        DPRINTF(EdgePredUnit, "TSF[tid:%i]: Sequential target is" 
//                " mispredicted, squashing the branch type predictor\n",
//                tid);

    if (predictorMode == Normal) {

        stage1->squash(block_pc, 
                       block_id,
                       is_taken,
                       tid);

        stage1->update(block_id,
                       block_pc,
                       tid,
                       is_taken,
                       seq_mispredicted);

    } else {
        assert(!seq_mispredicted);
        assert(is_taken);
    }
//    }

    if (is_taken) {
        stage2->update(block_id,
                       block_pc,
                       addr_space_id,
                       type_id,
                       tid);
    }

}

template<class Impl>
void
TwostageFramework<Impl>::squash(BlockID block_id, ThreadID tid)
{
    if (predictorMode == Normal) {
        stage1->squash(block_id, tid);
    }

    stage2->squash(block_id, tid);
}
